﻿using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Phone.Controls;

namespace Tequila.Phone.Controls
{
    /// <summary>
    /// Base page class that implements scrolling behind SIP keyboard
    /// </summary>
    public class SipScrollableFormPage : PhoneApplicationPage
    {
        private double _lastDelta;
        private bool _isSip;

        private ScrollViewer _scrollViewer;
        private FrameworkElement _layoutRoot;

        private Binding _transformBinding;

        private const double defaultScrollViewerSipModeHeight = 410;

        /// <summary>
        /// 
        /// </summary>
        public bool IsFromDormant
        {
            get; set;
        }

        private ScrollViewer FormScrollViewer
        {
            get { return _scrollViewer ?? (_scrollViewer = FindName(ScrollViewerName) as ScrollViewer); }
        }

        private FrameworkElement LayoutRoot
        {
            get { return _layoutRoot ?? (_layoutRoot = FindName(LayoutRootName) as FrameworkElement); }
        }


        /// <summary>
        /// Scroll viewer height for normal state, when SIP is not opened
        /// </summary>
        public double ScrollViewerFullModeHeight
        {
            get; set;
        }

        /// <summary>
        /// Scroll viewer height when SIP is opened
        /// </summary>
        public double ScrollViewerSipModeHeight { get; set; }
        
        /// <summary>
        /// Scroll viewer name
        /// </summary>
        public string ScrollViewerName { get; set; }
        
        /// <summary>
        /// Name of root control
        /// </summary>
        public string LayoutRootName { get; set; }


        /// <summary>
        /// RootFrame dependecy property
        /// </summary>
        public static readonly DependencyProperty RootFrameTransformProperty = DependencyProperty.Register(
            "RootFrameTransform",
            typeof(double),
            typeof(SipScrollableFormPage),
            new PropertyMetadata(OnRootFrameTransformChanged));

        /// <summary>
        /// SipScrollableFormPage constructor
        /// </summary>
        public SipScrollableFormPage()
        {
           Loaded += (sender, e) =>
                          {
                              _isSip = false;
                              IsFromDormant = false;
                              BindToLayoutRootTransformation();
                              PrepareSipControls();

                              ScrollViewerFullModeHeight = FormScrollViewer.ActualHeight;
                              if (ScrollViewerSipModeHeight == 0d)
                              {
                                  ScrollViewerSipModeHeight = defaultScrollViewerSipModeHeight;
                              }
                          };
        }

        private void BindToLayoutRootTransformation()
        {
            var frame = Application.Current.RootVisual as PhoneApplicationFrame;
            if (frame == null)
                return;
            _transformBinding = new Binding("Y") { Source = ((TransformGroup)frame.RenderTransform).Children[0] as TranslateTransform };
            SetBinding(RootFrameTransformProperty, _transformBinding);
        }

        private void PrepareSipControls()
        {
            var textboxes = FindVisualChildren<TextBox>(FormScrollViewer);
            foreach (var textbox in textboxes)
            {
                textbox.GotFocus += OnSipControlGotFocus;
                textbox.LostFocus += OnSipControlLostFocus;
            }

            var passwordboxes = FindVisualChildren<PasswordBox>(FormScrollViewer);
            foreach (var passwordbox in passwordboxes)
            {
                passwordbox.GotFocus += OnSipControlGotFocus;
                passwordbox.LostFocus += OnSipControlLostFocus;
            }
        }

        private void EnsureVisible(UIElement cc)
        {
            GeneralTransform gt = FormScrollViewer.TransformToVisual(cc);
            Point offset = gt.Transform(new Point(0, 30));

            var y = FormScrollViewer.VerticalOffset + (-offset.Y);
            var maxY = FormScrollViewer.ExtentHeight - FormScrollViewer.ViewportHeight;
            
            if (y > maxY)
            {
                y = maxY;
            }
            else if (y < 0)
            {
                y = 0;
            }

            FormScrollViewer.ScrollToVerticalOffset(y);
            FormScrollViewer.UpdateLayout();
        }

        private void OnSipControlGotFocus(object sender, RoutedEventArgs e)
        {
            if (_isSip)
            {
                var uiElement = sender as UIElement;
                if (uiElement != null)
                    EnsureVisible(uiElement);
                return;
            }
            
            FormScrollViewer.Height = ScrollViewerSipModeHeight;
            FormScrollViewer.UpdateLayout();

            var element = sender as UIElement;
            if (element != null)
                EnsureVisible(element);
            _isSip = true;
        }

        private void OnSipControlLostFocus(object sender, RoutedEventArgs e)
        {
            if(IsFromDormant)
            {
                var ctrl = sender as Control;
                if (ctrl != null)
                    ctrl.Focus();
                IsFromDormant = false;
                return;
            }
            var focusedElement = FocusManager.GetFocusedElement();
            if (focusedElement is TextBox || focusedElement is PasswordBox)
                return;

            FormScrollViewer.Height = ScrollViewerFullModeHeight;
            _isSip = false;
        }

        private static void OnRootFrameTransformChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            var page = (SipScrollableFormPage)source;
            var verticalOffset = -1 * ((double)e.NewValue);
            page.OnRootFrameTransformChanged(verticalOffset);
        }

        private void OnRootFrameTransformChanged(double verticalOffest)
        {
            // 1. ignore transformation caused by SIP - override it with margin
            LayoutRoot.Margin = new Thickness(0, verticalOffest, 0, 0);

            // 2. handle open/close SIP and update ScrollViewer's Height & VerticalOffset
            if (verticalOffest > 0 && verticalOffest > _lastDelta)
            {
                // move position
                _isSip = true;
            }

            _lastDelta = verticalOffest;
        }

        private static IList<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject
        {
            var result = new List<T>();
            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {

                var child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                    result.Add((T)child);

                var childendOfChild = FindVisualChildren<T>(child);
                if (childendOfChild.Count > 0)
                    result.AddRange(childendOfChild);
            }

            return result;
        }
    }
}